package com.futurescale.sa.model.vo
{
	/**
	 * A base class for Value Object
	 */
	[Bindable]
	public class ValueObject
	{
		/**
		 * Constants for separation of concatenated text.
		 */
		public static const DOUBLE_SPACE:String = "\n\n";
		public static const DASH:String = "-";
		public static const SPACE:String = " ";
		
		/**
		 * Number of unique Value Objects created this runtime. 
		 * Used to ensure two objects of the same type created
		 * within the same millisecond will have a unique uid.
		 */
		protected static var uidRuntimeUniquifier:Number = 0;
		
		protected var x:XML;
		
		/**
		 * Construct a ValueObject. The element name must be passed
		 * in, but the XML is optional. The XML will be validated to
		 * be of the element type defined, or replaced with a default
		 * XML element of the given type.  
		 */
		public function ValueObject( element :String, xml :XML = null )
		{
			this.xml = validateOrProvideDefault( element, xml );
		}
		
		/**
		 * XML representation of Value Object. Override 
		 * accessors to provide default.
		 */
		public function set xml( x:XML ):void
		{
			this.x = x;
		}
		
		public function get xml():XML
		{
			return x;    
		}
		
		/**
		 * Validate that the XML element passed in is not null and that
		 * its element name is correct. Otherwise provide a default with a
		 * unique identifier.
		 */ 
		protected function validateOrProvideDefault( elementName:String, xml:XML ):XML
		{
			if (xml == null || xml.localName() != elementName) {
				var u:String = elementName + DASH + new Date().time + 
					DASH + uidRuntimeUniquifier++;
				xml = <{elementName} uid={u}/>
			}
			return xml;
		}
		
		/**
		 * Get a stub of this Value Object.
		 * 
		 * Certain objects need to be stubbed in the parent XML
         * structure before the parent can be saved. For instance, 
         * all the Story elements in a Series or the Cast and Milieu
         * in both the Story and Series. 
         */
        public function getStub( ):XML
        {
            var stub:XML = validateOrProvideDefault( xml.localName() );
            var vo:ValueObject = new ValueObject( xml.localName(), stub );
            vo.isStub = true;
            vo.uid = this.uid;
            vo.name = this.name;
            vo.ordinal = this.ordinal;
            vo.description = this.description;
            return stub;
        }
		
		/**
		 * The name of the Value Object. If no name is set,
		 * element and ordinal attribute are concatenated.
		 */
		public function set name( n:String ):void
		{
			xml.name = n;    
		}
		public function get name():String
		{
			return (String(xml.name)!="") ? 
				String(xml.name) : String(xml.localName()+SPACE+ordinal);
		}
		
		/**
		 * Ordinal location of this Value Object within the collection.
		 */
		public function set ordinal( o:Number ):void
		{
			xml.@ordinal = o;    
		}
		public function get ordinal():Number
		{
			return Number(xml.@ordinal);
		}
		
		/**
		 * Unique identifier for this ValueObject
		 */
		public function set uid( u:String ):void
		{
			xml.@uid = u;
		}
		public function get uid():String
		{
			return String(xml.@uid);
		}
		
		/**
		 * Is this ValueObject an unpopulated stub?
		 */
		public function set isStub( s:Boolean ):void
		{
			xml.@isStub = s;
		}
		public function get isStub():Boolean
		{
			return (x.@isStub=="true");
		}
		/**
		 * A short description of this ValueObject
		 */
		public function set description( d:String ):void
		{
			xml.description = d;
		}
		public function get description():String
		{
			return String(xml.description);
		}
		
		/** 
		 * An Vector of a ValueObject's NoteVOs.
		 */
		public function get notes():Vector.<NoteVO>
		{
			var v:Vector.<NoteVO> = new Vector.<NoteVO>();
			var xl:XMLList = xml.child(NoteVO.ELEMENT);
			for each ( var nx:XML in xl ) {
				var note:NoteVO = new NoteVO(nx);
				v.push(note);
			}
			return v;
		}
		public function set notes( v:Vector.<NoteVO> ):void 
		{} // read-only but bindable
		
		/**
		 * Add a Note to the ValueObject
		 */
		public function addNote( note:NoteVO ):void
		{
			xml.appendChild( note.xml );
		}
		
		/**
		 * A function for sorting ValueObjects by ordinal.
		 */
		public static function ordinalSort( v1:ValueObject, v2:ValueObject ):Number
		{
			var retval:Number = 0;                             // equality
			if (v1.ordinal < v2.ordinal) { retval = -1; }      // less
			else if (v1.ordinal > v2.ordinal) { retval = 1; }  // greater
			return retval;
		}
		/**
		 * A function for sorting ValueObjects by name.
		 */
		public static function nameSort( v1:ValueObject, v2:ValueObject ):Number
		{
			var retval:Number = 0;                          // equality
			if (v1.name < v2.name) { retval = -1; }         // less
			else if (v1.name > v2.name) { retval = 1; }     // greater
			return retval;
		}
		
		/**
		 * Get the next ordinal for a child element.
		 */
		protected function getNextOrdinal( xl:XMLList ):Number
		{
			var n:Number = 0;
			var x:XML;
			for each ( x in xl ) {
				var o:Number = Number(x.@ordinal);
				if (o > n) n = o;
			}
			return ++n;
		}
	}
}